<?php

namespace jjl\yii2\db;

use Yii;
use yii\base\Model;

class DbLog extends Model {

    use InitTrait;

    public function init() {
        $this->loadTranslations();
        parent::init();
    }

    const OPERATION_TYPE = ['insert', 'update', 'delete', 'recovery'];

    private $_db;
    private $_table;
    public $id;
    public $uuid;
    public $object;
    public $operator;
    public $time;
    public $type;

    public function setDb($value) {
        $this->_db = $value;
    }

    public function rules() {
        return [
            [['object', '_table'], 'safe'],
            [['id', 'uuid', 'object', 'type'], 'required'],
            [['id', 'operator', 'time'], 'integer'],
            ['uuid', 'string', 'max' => 50],
            ['type', 'in', 'range' => self::OPERATION_TYPE],
            ['operator', 'default', 'value' => Yii::$app->user->id],
            ['time', 'default', 'value' => time()],
        ];
    }

    public function attributeLabels() {
        return [
            'id' => Yii::t('yii2db', 'ID'),
            'uuid' => Yii::t('yii2db', 'UUID'),
            'object' => Yii::t('yii2db', 'Object'),
            'operator' => Yii::t('yii2db', 'Operator'),
            'time' => Yii::t('yii2db', 'Operation Time'),
            'type' => Yii::t('yii2db', 'Operation Type'),
        ];
    }

    /**
     * 
     * @param string $object
     * @return $this
     */
    public function mapping(string $object) {
        if (class_exists($object))
            $this->_table = $object::tableName();
        else
            $this->_table = $object;
        return $this;
    }

    public function insert(Core $model) {
        $this->mapping(get_class($model));
        $this->setAttributes([
            'id' => $model->id,
            'uuid' => $model->uuid,
            'object' => $model->getAttributes(),
            'type' => 'insert',
        ]);
        $this->save();
    }

    public function update(Core $model) {
        $this->mapping(get_class($model));
        $this->setAttributes([
            'id' => $model->id,
            'uuid' => $model->uuid,
            'object' => $model->getAttributes(),
            'type' => 'update',
        ]);
        $this->save();
    }

    public function delete(Core $model) {
        $this->mapping(get_class($model));
        $this->setAttributes([
            'id' => $model->id,
            'uuid' => $model->uuid,
            'object' => $model->getAttributes(),
            'type' => 'delete',
        ]);
        $this->save();
    }

    public function recovery() {
        $keys = implode(',', array_keys($this->object));
        $values = implode(',', array_map(function($value) {
                    return "\"{$value}\"";
                }, array_values($this->object)));
        $sql = "replace into {$this->_table} ({$keys}) values ({$values})";
        Yii::$app->db->createCommand($sql)->execute();
        $_this = clone $this;
        $_this->setAttributes([
            'type' => 'recovery',
            'operator' => null,
            'time' => null,
        ]);
        $_this->save();
    }

    /**
     * 保存
     * @return boolean
     */
    private function save() {
        assert((bool) Helper::tableExists($this->_table, $this->db), "日志表{$this->_table}不存在");
        if (!$this->validate())
            return false;
        return (bool) $this->db->createCommand()->insert($this->_table, $this->getAttributes())->execute();
    }

    /**
     * 获取数据库句柄 
     * @return \yii\db\Connection the database connection.
     */
    public function getDb() {
        return Yii::$app->{$this->_db};
    }

    /**
     * return \yii\db\Query
     */
    public function find() {
        return (new \yii\db\Query())->from($this->_table);
    }

    /**
     * 查找历史
     * @param string $className
     * @param int|string|array $condition
     * @return \jjl\yii2\db\DbLog
     */
    public function findOne($condition) {
        $query = $this->find();
        if (is_array($condition)) {
            $arr = $condition; // array
            $condition = json_encode($condition);
        } else if (is_numeric($condition)) {
            $id = $condition; // id
        } else if (Helper::is_uuid($condition)) {
            $uuid = $condition; // uuid
        } else {
            $sql = $condition; // sql
        }
        if (!($data = Yii::$app->dbCache->store('log')->get($this->_table . "#{$condition}"))) {
            if (isset($id))
                $query->where(['id' => $id]);
            else if (isset($uuid))
                $query->where(['uuid' => $uuid]);
            else if (isset($sql))
                $query->where($sql);
            else if (isset($arr))
                $query->where($arr);
            else
                $query->where('1=0');
            if ($data = $query->one($this->db)) {
                Yii::$app->dbCache->store('log')->set($this->_table . "#{$condition}", $data);
            }
        }
        if ($data) {
            $model = new self($data);
            $model->afterFind();
            return $model;
        }
    }

    public function afterFind() {
        $this->object = json_decode($this->object, true);
    }

    public function getUser() {
        return \jjl\yii2\log\models\User::findOne($this->operator);
    }

}
